/**
 * @author erond
 */

package master;

import java.io.*;
import java.net.*;
import java.sql.Timestamp;
import java.util.Observable;

import btInquiryRssi.BtInquiryRssi;
import btInquiryRssi.InitBluetoothDeviceThread;
import btInquiryRssi.ShutdownThread;


/**
 * In questa classe vengono creati ed avviati i thread relativi alle varie sezioni del MASTER, ovvero:
 *
 *  - BeforeGatewayLinkThread
 *  - AfterGatewayLinkThread
 *  - MasterJobServerThread
 *  
 *  Inoltre viene lanciata l'interfaccia per i settaggi di bootstrap, e successivamente il monitor di sistema
 * 
 */
public class Master extends Observable implements Runnable{
	
	private int masterJobPort;
	/**
	 * @return the masterJobPort
	 */
	public int getMasterJobPort() {
		return masterJobPort;
	}

	/**
	 * @param masterJobPort the masterJobPort to set
	 */
	public void setMasterJobPort(int masterJobPort) {
		this.masterJobPort = masterJobPort;
	}

	/**
	 * @return the server_ServerInputPort
	 */
	public int getServer_ServerInputPort() {
		return beforeNeighbour_ServerInputPort;
	}

	/**
	 * @param serverServerInputPort the server_ServerInputPort to set
	 */
	public void setServer_ServerInputPort(int serverServerInputPort) {
		beforeNeighbour_ServerInputPort = serverServerInputPort;
	}

	/**
	 * @return the client_ServerInputPort
	 */
	public int getClient_ServerInputPort() {
		return afterNeighbour_ServerInputPort;
	}

	/**
	 * @param clientServerInputPort the client_ServerInputPort to set
	 */
	public void setClient_ServerInputPort(int clientServerInputPort) {
		afterNeighbour_ServerInputPort = clientServerInputPort;
	}

	/**
	 * @return the slaveIP
	 */
	public InetAddress getSlaveIP() {
		return slaveIP;
	}

	/**
	 * @param slaveIP the slaveIP to set
	 */
	public void setSlaveIP(InetAddress slaveIP) {
		this.slaveIP = slaveIP;
	}

	/**
	 * @return the slaveDeleteServerPort_after
	 */
	public int getSlaveDeleteServerPort_after() {
		return slaveDeleteServerPort_after;
	}

	/**
	 * @param slaveDeleteServerPortAfter the slaveDeleteServerPort_after to set
	 */
	public void setSlaveDeleteServerPort_after(int slaveDeleteServerPortAfter) {
		slaveDeleteServerPort_after = slaveDeleteServerPortAfter;
	}

	/**
	 * @return the slaveDeleteServerPort_before
	 */
	public int getSlaveDeleteServerPort_before() {
		return slaveDeleteServerPort_before;
	}

	/**
	 * @param slaveDeleteServerPortBefore the slaveDeleteServerPort_before to set
	 */
	public void setSlaveDeleteServerPort_before(int slaveDeleteServerPortBefore) {
		slaveDeleteServerPort_before = slaveDeleteServerPortBefore;
	}

	/**
	 * @return the masterJobServerSocket
	 */
	public ServerSocket getMasterServerSocket() {
		return masterJobServerSocket;
	}

	/**
	 * @param masterJobServerSocket the masterJobServerSocket to set
	 */
	public void setMasterServerSocket(ServerSocket masterServerSocket) {
		this.masterJobServerSocket = masterServerSocket;
	}

	/**
	 * @return the afterGatewayLink_ServerInputSocket
	 */
	public ServerSocket getGatewayServer_ServerInputSocket() {
		return afterGatewayLink_ServerInputSocket;
	}

	/**
	 * @param gatewayServerServerInputSocket the afterGatewayLink_ServerInputSocket to set
	 */
	public void setGatewayServer_ServerInputSocket(
			ServerSocket gatewayServerServerInputSocket) {
		afterGatewayLink_ServerInputSocket = gatewayServerServerInputSocket;
	}

	/**
	 * @return the beforeGatewayLink_ServerInputSocket
	 */
	public ServerSocket getGatewayClient_ServerInputSocket() {
		return beforeGatewayLink_ServerInputSocket;
	}

	/**
	 * @param gatewayClientServerInputSocket the beforeGatewayLink_ServerInputSocket to set
	 */
	public void setGatewayClient_ServerInputSocket(
			ServerSocket gatewayClientServerInputSocket) {
		beforeGatewayLink_ServerInputSocket = gatewayClientServerInputSocket;
	}

	/**
	 * @return the gatewayServer_ClientOutputSocket
	 */
	public Socket getGatewayServer_ClientOutputSocket() {
		return afterGatewayLink_ClientOutputSocket;
	}

	/**
	 * @param gatewayServerClientOutputSocket the gatewayServer_ClientOutputSocket to set
	 */
	public void setGatewayServer_ClientOutputSocket(
			Socket gatewayServerClientOutputSocket) {
		afterGatewayLink_ClientOutputSocket = gatewayServerClientOutputSocket;
	}

	/**
	 * @return the gatewayClient_ClientOutputSocket
	 */
	public Socket getGatewayClient_ClientOutputSocket() {
		return beforeGatewayLink_ClientOutputSocket;
	}

	/**
	 * @param gatewayClientClientOutputSocket the gatewayClient_ClientOutputSocket to set
	 */
	public void setGatewayClient_ClientOutputSocket(
			Socket gatewayClientClientOutputSocket) {
		beforeGatewayLink_ClientOutputSocket = gatewayClientClientOutputSocket;
	}

	/**
	 * @return the beforeGatewayLink
	 */
	public BeforeGatewayLink getBeforeGatewayLink() {
		return beforeGatewayLink;
	}

	/**
	 * @param beforeGatewayLink the beforeGatewayLink to set
	 */
	public void setBeforeGatewayLink(BeforeGatewayLink beforeGatewayLink) {
		this.beforeGatewayLink = beforeGatewayLink;
	}

	/**
	 * @return the afterGatewayLink
	 */
	public AfterGatewayLink getAfterGatewayLink() {
		return afterGatewayLink;
	}

	/**
	 * @param afterGatewayLink the afterGatewayLink to set
	 */
	public void setAfterGatewayLink(AfterGatewayLink afterGatewayLink) {
		this.afterGatewayLink = afterGatewayLink;
	}

	/**
	 * @return the masterDeleteClient_BeforeLinkThread
	 */
	public MasterDeleteClient_BeforeLinkThread getMasterDeleteClient_BeforeLinkThread() {
		return masterDeleteClient_BeforeLinkThread;
	}

	/**
	 * @param masterDeleteClientGatewayClientThread the masterDeleteClient_BeforeLinkThread to set
	 */
	public void setMasterDeleteClient_BeforeLinkThread(
			MasterDeleteClient_BeforeLinkThread masterDeleteClientGatewayClientThread) {
		masterDeleteClient_BeforeLinkThread = masterDeleteClientGatewayClientThread;
	}

	/**
	 * @return the masterDeleteClient_AfterLinkThread
	 */
	public MasterDeleteClient_AfterLinkThread getMasterDeleteClient_AfterLinkThread() {
		return masterDeleteClient_AfterLinkThread;
	}

	/**
	 * @param masterDeleteClientGatewayServerThread the masterDeleteClient_AfterLinkThread to set
	 */
	public void setMasterDeleteClient_AfterLinkThread(
			MasterDeleteClient_AfterLinkThread masterDeleteClientGatewayServerThread) {
		masterDeleteClient_AfterLinkThread = masterDeleteClientGatewayServerThread;
	}

	/**
	 * @return the btInquiryRssi
	 */
	public BtInquiryRssi getBtInquiryRssi() {
		return btInquiryRssi;
	}

	/**
	 * @param btInquiryRssi the btInquiryRssi to set
	 */
	public void setBtInquiryRssi(BtInquiryRssi btInquiryRssi) {
		this.btInquiryRssi = btInquiryRssi;
	}

	/**
	 * @return the initBluetoothDeviceThread
	 */
	public InitBluetoothDeviceThread getInitBluetoothDeviceThread() {
		return initBluetoothDeviceThread;
	}

	/**
	 * @param initBluetoothDeviceThread the initBluetoothDeviceThread to set
	 */
	public void setInitBluetoothDeviceThread(
			InitBluetoothDeviceThread initBluetoothDeviceThread) {
		this.initBluetoothDeviceThread = initBluetoothDeviceThread;
	}

	/**
	 * @return the shutDownThread
	 */
	public ShutdownThread getShutDownThread() {
		return shutDownThread;
	}

	/**
	 * @param shutDownThread the shutDownThread to set
	 */
	public void setShutDownThread(ShutdownThread shutDownThread) {
		this.shutDownThread = shutDownThread;
	}

	/**
	 * @return the master
	 */
	public Master getMaster() {
		return master;
	}

	/**
	 * @param master the master to set
	 */
	public void setMaster(Master master) {
		this.master = master;
	}

	/**
	 * @return the afterGatewayLinkThread
	 */
	public Thread getAfterGatewayLinkThread() {
		return afterGatewayLinkThread;
	}

	/**
	 * @param afterGatewayLinkThread the afterGatewayLinkThread to set
	 */
	public void setGatewayServerThread(Thread gatewayServerThread) {
		this.afterGatewayLinkThread = gatewayServerThread;
	}

	/**
	 * @return the beforeGatewayLinkThread
	 */
	public Thread getBeforeGatewayLinkThread() {
		return beforeGatewayLinkThread;
	}

	/**
	 * @param beforeGatewayLinkThread the beforeGatewayLinkThread to set
	 */
	public void setGatewayClientThread(Thread gatewayClientThread) {
		this.beforeGatewayLinkThread = gatewayClientThread;
	}

	/**
	 * @return the masterGatewayThread
	 */
	public Thread getMasterGatewayThread() {
		return masterGatewayThread;
	}

	/**
	 * @param masterGatewayThread the masterGatewayThread to set
	 */
	public void setMasterGatewayThread(Thread masterGatewayThread) {
		this.masterGatewayThread = masterGatewayThread;
	}

	/**
	 * @return the enableAfter
	 */
	public boolean isEnableAfter() {
		return enableAfter;
	}

	/**
	 * @param enableAfter the enableAfter to set
	 */
	public void setEnableAfter(boolean enableAfter) {
		this.enableAfter = enableAfter;
	}

	/**
	 * @return the enableBefore
	 */
	public boolean isEnableBefore() {
		return enableBefore;
	}

	/**
	 * @param enableBefore the enableBefore to set
	 */
	public void setEnableBefore(boolean enableBefore) {
		this.enableBefore = enableBefore;
	}

	/**
	 * @param masterJobServer the masterJobServer to set
	 */
	public void setMasterJobServer(MasterJobServer masterJobServer) {
		this.masterJobServer = masterJobServer;
	}

	/**
	 * @param masterJobServerThread the masterJobServerThread to set
	 */
	public void setMasterJobServerThread(Thread masterJobServerThread) {
		this.masterJobServerThread = masterJobServerThread;
	}

	private int beforeNeighbour_ServerInputPort;
	private InetAddress beforeNeighbour_ServerIP;
	private int afterNeighbour_ServerInputPort;
	private InetAddress afterNeighbour_ServerIP;
	/**
	 * @return the server_ServerIP
	 */
	public InetAddress getServer_ServerIP() {
		return beforeNeighbour_ServerIP;
	}

	/**
	 * @param serverServerIP the server_ServerIP to set
	 */
	public void setServer_ServerIP(InetAddress serverServerIP) {
		beforeNeighbour_ServerIP = serverServerIP;
	}

	/**
	 * @return the client_ServerIP
	 */
	public InetAddress getClient_ServerIP() {
		return afterNeighbour_ServerIP;
	}

	/**
	 * @param clientServerIP the client_ServerIP to set
	 */
	public void setClient_ServerIP(InetAddress clientServerIP) {
		afterNeighbour_ServerIP = clientServerIP;
	}

	private String networkInterface = null;
	
	private InetAddress slaveIP = null;
	private int slaveDeleteServerPort_after;
	private int slaveDeleteServerPort_before;
	private ServerSocket masterJobServerSocket = null;
	private ServerSocket afterGatewayLink_ServerInputSocket = null;
	private ServerSocket beforeGatewayLink_ServerInputSocket = null;
	private Socket afterGatewayLink_ClientOutputSocket = null;
	private Socket beforeGatewayLink_ClientOutputSocket = null;
	private BeforeGatewayLink beforeGatewayLink = null;
	private AfterGatewayLink afterGatewayLink = null;
	private MasterDeleteClient_BeforeLinkThread masterDeleteClient_BeforeLinkThread = null;
	private MasterDeleteClient_AfterLinkThread masterDeleteClient_AfterLinkThread = null;
	private MasterJobServer masterJobServer = null;
	private BtInquiryRssi btInquiryRssi = null;
	private InitBluetoothDeviceThread initBluetoothDeviceThread = null;
	private ShutdownThread shutDownThread = null;
	private Master master = null;
	private Thread masterJobServerThread = null;
	private Thread afterGatewayLinkThread = null;
	private Thread beforeGatewayLinkThread = null;
	private Thread masterGatewayThread = null;
	private boolean enableAfter = true;
	private boolean enableBefore = true;
	public static MasterMonitor monitor = null;
	private InetAddress beforeMulticastIP = null;
	private InetAddress afterMulticastIP = null;
	private int beforeMulticastPort;
	private int afterMulticastPort;
	private int inquiryMaxTime = 0;
	private int inquiryMaxPacket = 0;
	private InetAddress slaveMulticastIP = null;
	private int slaveMulticastPort;
	private int hciDev;
	private String defaultFileName = "";
	private int inquiryThresholding = 0;
	
	/**
	 * @return the defaultFileName
	 */
	public String getDefaultFileName() {
		return defaultFileName;
	}

	/**
	 * @param defaultFileName the defaultFileName to set
	 */
	public void setDefaultFileName(String defaultFileName) {
		this.defaultFileName = defaultFileName;
	}

	/**
	 * @return the inquiryThresholding
	 */
	public int getInquiryThresholding() {
		return inquiryThresholding;
	}

	/**
	 * @param inquiryThresholding the inquiryThresholding to set
	 */
	public void setInquiryThresholding(int inquiryThresholding) {
		this.inquiryThresholding = inquiryThresholding;
	}

	/**
	 * @param hciDev the hciDev to set
	 */
	public void setHciDev(int hciDev) {
		this.hciDev = hciDev;
	}

	/**
	 * @return the hciDev
	 */
	public int getHciDev() {
		return hciDev;
	}

	/**
	 * @return the slaveMulticastIP
	 */
	public InetAddress getSlaveMulticastIP() {
		return slaveMulticastIP;
	}

	/**
	 * @param slaveMulticastIP the slaveMulticastIP to set
	 */
	public void setSlaveMulticastIP(InetAddress slaveMulticastIP) {
		this.slaveMulticastIP = slaveMulticastIP;
	}

	/**
	 * @return the slaveMulticastPort
	 */
	public int getSlaveMulticastPort() {
		return slaveMulticastPort;
	}

	/**
	 * @param slaveMulticastPort the slaveMulticastPort to set
	 */
	public void setSlaveMulticastPort(int slaveMulticastPort) {
		this.slaveMulticastPort = slaveMulticastPort;
	}

	/**
	 * @return the inquiryMaxTime
	 */
	public int getInquiryMaxTime() {
		return inquiryMaxTime;
	}

	/**
	 * @param inquiryMaxTime the inquiryMaxTime to set
	 */
	public void setInquiryMaxTime(int inquiryMaxTime) {
		this.inquiryMaxTime = inquiryMaxTime;
	}

	/**
	 * @return the inquiryMaxPacket
	 */
	public int getInquiryMaxPacket() {
		return inquiryMaxPacket;
	}

	/**
	 * @param inquiryMaxPacket the inquiryMaxPacket to set
	 */
	public void setInquiryMaxPacket(int inquiryMaxPacket) {
		this.inquiryMaxPacket = inquiryMaxPacket;
	}

	/**
	 * @return the beforeMulticastIP
	 */
	public InetAddress getBeforeMulticastIP() {
		return beforeMulticastIP;
	}

	/**
	 * @param beforeMulticastIP the beforeMulticastIP to set
	 */
	public void setBeforeMulticastIP(InetAddress beforeMulticastIP) {
		this.beforeMulticastIP = beforeMulticastIP;
	}

	/**
	 * @return the afterMulticastIP
	 */
	public InetAddress getAfterMulticastIP() {
		return afterMulticastIP;
	}

	/**
	 * @param afterMulticastIP the afterMulticastIP to set
	 */
	public void setAfterMulticastIP(InetAddress afterMulticastIP) {
		this.afterMulticastIP = afterMulticastIP;
	}

	/**
	 * @return the beforeMulticastPort
	 */
	public int getBeforeMulticastPort() {
		return beforeMulticastPort;
	}

	/**
	 * @param beforeMulticastPort the beforeMulticastPort to set
	 */
	public void setBeforeMulticastPort(int beforeMulticastPort) {
		this.beforeMulticastPort = beforeMulticastPort;
	}

	/**
	 * @return the afterMulticastPort
	 */
	public int getAfterMulticastPort() {
		return afterMulticastPort;
	}

	/**
	 * @param afterMulticastPort the afterMulticastPort to set
	 */
	public void setAfterMulticastPort(int afterMulticastPort) {
		this.afterMulticastPort = afterMulticastPort;
	}

	/**
	 * @param networkInterface the networkInterface to set
	 */
	public void setNetworkInterface(String networkInterface) {
		this.networkInterface = networkInterface;
	}

	/**
	 * @return the networkInterface
	 */
	public String getNetworkInterface() {
		return networkInterface;
	}

	public Master() {}

	public MasterJobServer getMasterJobServer() {
		return masterJobServer;
	}

	public Thread getMasterJobServerThread() {
		return masterJobServerThread;
	}

	public void run() {

		synchronized (this) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		monitor = new MasterMonitor("Master Monitor",this, getInquiryMaxTime(), getInquiryMaxPacket(), getInquiryThresholding());

		addObserver(monitor);
		setChanged();
		notifyObservers("Master: avviato..\n");

		System.out.println("Master: avviato..\n");

		/**
		 * Inizializzazione hardware bluetooth
		 */
		btInquiryRssi = new BtInquiryRssi();
		initBluetoothDeviceThread = new InitBluetoothDeviceThread(btInquiryRssi, hciDev);
		shutDownThread = new ShutdownThread(btInquiryRssi);

		initBluetoothDeviceThread.start();
		try {
			initBluetoothDeviceThread.join();
		} catch (InterruptedException e2) {
			e2.printStackTrace();
		}

		try {

			/**
			 * Creazione delle Server Socket presenti sul Master
			 */
			afterGatewayLink_ServerInputSocket = new ServerSocket(beforeNeighbour_ServerInputPort);
			afterGatewayLink_ServerInputSocket.setReuseAddress(true);

			setChanged();
			notifyObservers("Ho creato la afterGatewayLink_ServerInputSocket: "+afterGatewayLink_ServerInputSocket.toString());

			beforeGatewayLink_ServerInputSocket = new ServerSocket(afterNeighbour_ServerInputPort);
			beforeGatewayLink_ServerInputSocket.setReuseAddress(true);

			setChanged();
			notifyObservers("Ho creato la beforeGatewayLink_ServerInputSocket: "+beforeGatewayLink_ServerInputSocket.toString());

		} catch (IOException e) {
			e.printStackTrace();
			System.exit(2);
		}

		/**
		 * Creazione link con Gateway successivo
		 */
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		afterGatewayLink = new AfterGatewayLink(afterGatewayLink_ServerInputSocket,afterNeighbour_ServerInputPort,afterNeighbour_ServerIP, slaveIP, slaveDeleteServerPort_after,monitor);

		afterGatewayLinkThread = new Thread(afterGatewayLink);
		afterGatewayLinkThread.setName("afterGatewayLinkThread");
		afterGatewayLinkThread.start();


		/**
		 * Creazione link con Gateway precedente
		 */
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		beforeGatewayLink = new BeforeGatewayLink(beforeGatewayLink_ServerInputSocket, beforeNeighbour_ServerInputPort,beforeNeighbour_ServerIP, slaveIP, slaveDeleteServerPort_before, monitor);

		beforeGatewayLinkThread = new Thread(beforeGatewayLink);
		beforeGatewayLinkThread.setName("beforeGatewayLinkThread");
		beforeGatewayLinkThread.start();

		/**
		 * Creazione link (socket e thread) con lo SLAVE
		 */
		try {
			masterJobServerSocket = new ServerSocket(masterJobPort);
			masterJobServerSocket.setReuseAddress(true);
		} catch (IOException ioe1) {
			ioe1.printStackTrace();
			try {
				afterGatewayLink_ServerInputSocket.close();
				beforeGatewayLink_ServerInputSocket.close();	
				masterJobServerSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.exit(3);
		}

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		setChanged();
		notifyObservers("Ho creato la masterJobServerSocket: "+masterJobServerSocket);

		masterJobServer = new MasterJobServer(btInquiryRssi, hciDev, masterJobServerSocket,
				beforeGatewayLink,afterGatewayLink, slaveIP,
				monitor, enableBefore, enableAfter, beforeMulticastIP,
				afterMulticastIP, beforeMulticastPort, afterMulticastPort,
				inquiryMaxTime, inquiryMaxPacket, inquiryThresholding,
				slaveMulticastIP, slaveMulticastPort, networkInterface, defaultFileName);
		
		masterJobServerThread = new Thread(masterJobServer);
		masterJobServerThread.setName("masterJobServerThread");
		masterJobServerThread.start(); 

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}

		try {
			/**
			 * Attesa terminazione thread principali (che dovrebbe avvenire SOLO in caso di errore)
			 */
			masterJobServerThread.join();
			beforeGatewayLinkThread.join();
			afterGatewayLinkThread.join();

		} catch (InterruptedException ie) {
			System.err.println("Eccezione nella join di un thread: \n");
			ie.printStackTrace();
		}

	}//fine run

	public static void main(String[] args) throws IOException {
		
		Timestamp timestamp = new Timestamp(System.currentTimeMillis());
		System.out.println("");
		System.out.println("                     ["+timestamp+"]");
		System.out.println("*******************************************************************");
		System.out.println("*                             BRADiPO                             *");
		System.out.println("*                                                                 *");
		System.out.println("*        Bluetooth Rtp Audio DIspatcher Proximity Oriented        *");
		System.out.println("*      Roberto Coluccio - Alex Di Mango - Primiano Ciavarella     *");
		System.out.println("*Progetto d'esame 2009 - Reti di Calcolatori LS - Prof. A. Corradi*");
		System.out.println("*******************************************************************");
		System.out.println("");
		
		/**
		 * Avvio l'interfaccia di bootstrap e ne attendo la terminazione (pressione START)
		 */

		Master master = new Master();
		Thread masterThread = new Thread(master);
		masterThread.start();
		master.setMaster(master);

		MasterSetting setting = new MasterSetting("Master bootstrap setting",master);
		Thread settingThread = new Thread(setting);
		settingThread.start();
		System.err.println("Avvio l'interfaccia di bootstrap e ne attendo la terminazione (pressione START)");

		try {
			masterThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}//fine main

}//fine Master
